Prompt

Prompt

什么是Prompt

  • 在上一篇已经记录了提示词主要是通过Message来实现结构化,且Message会存在多个。
  • 如果需要执行过程中操作这些Message,一个一个来就会变得繁琐。
  • 所以SpringAI将Message按顺序聚合在Prompt内,再提供处操作Message的能力。
  • Prompt除了Message还组合了ChatOptions,这个在后续模型中再学习记录。
  • 还有自身复制和异变等。

Prompt的能力

  • 使用构造者模式创建Prompt

  • 增强/修改SystemMessageUserMessage,每次操作会都会复制一个新的Prompt

  • 获取所有的Message

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    private final ChatClient promptClient;

    public void promptExample() {
    // 构造者模式创建Prompt
    Prompt prompt = Prompt.builder()
    .messages(
    // 用英文作为系统提示词
    SystemMessage.builder().text("你是一位Java专家。").build(),
    // 用户输入了Deque类,默认也是英文
    UserMessage.builder().text("SpringAI有哪些优势").build()
    ).build();

    log.info("\nprompt text -> {}", prompt.getContents());

    // 增强/修改 SystemMessage
    Prompt augmentSystemPrompt = prompt.augmentSystemMessage(systemMessage -> {
    return SystemMessage.builder()
    .text(systemMessage.getText() + "回答问题要精简,不需要做过多介绍跟解释。")
    .build();
    });

    log.info("\naugmentSystemPrompt text -> {}", augmentSystemPrompt.getContents());

    // 增强/修改 UserMessage
    Prompt augmentUserPrompt = augmentSystemPrompt.augmentUserMessage(userMessage ->
    UserMessage.builder()
    .text("帮我解释一下:" + userMessage.getText())
    .build()
    );

    log.info("\naugmentUserPrompt text -> {}", augmentUserPrompt.getContents());

    // 获取所有的 Message
    SystemMessage systemMessage = augmentUserPrompt.getSystemMessage();
    List<UserMessage> userMessages = augmentUserPrompt.getUserMessages();
    List<Message> instructions = augmentUserPrompt.getInstructions();
    log.info("\nsystemMessage -> {}\nuserMessages -> {}\ninstructions -> {}", systemMessage, userMessages, instructions);
    }
  • 创建&修改Message的输出。可以看到通过augmentXxxPrompt每次都有变更掉Message内容

    1
    2
    3
    4
    5
    6
    7
    2025-07-02T15:20:01.646+08:00  INFO 21138 --- [spring-ai-example] [           main] c.s.ai.example.prompt.two.PromptExample  : 
    prompt text -> 你是一位Java专家。SpringAI有哪些优势
    2025-07-02T15:20:01.648+08:00 INFO 21138 --- [spring-ai-example] [ main] c.s.ai.example.prompt.two.PromptExample :
    augmentSystemPrompt text -> 你是一位Java专家。回答问题要精简,不需要做过多介绍跟解释。SpringAI有哪些优势
    2025-07-02T15:20:01.649+08:00 INFO 21138 --- [spring-ai-example] [ main] c.s.ai.example.prompt.two.PromptExample :
    augmentUserPrompt text -> 你是一位Java专家。回答问题要精简,不需要做过多介绍跟解释。帮我解释一下:SpringAI有哪些优势

  • 获取所有的Message输出。可以获取到systemMessage\userMessages或者所有的。

    1
    2
    3
    4
    5
    6
    2025-07-02T15:20:01.649+08:00  INFO 21138 --- [spring-ai-example] [           main] c.s.ai.example.prompt.two.PromptExample  : 
    systemMessage -> SystemMessage{textContent='你是一位Java专家。回答问题要精简,不需要做过多介绍跟解释。', messageType=SYSTEM, metadata={messageType=SYSTEM}}

    userMessages -> [UserMessage{content='帮我解释一下:SpringAI有哪些优势', properties={messageType=USER}, messageType=USER}]

    instructions -> [SystemMessage{textContent='你是一位Java专家。回答问题要精简,不需要做过多介绍跟解释。', messageType=SYSTEM, metadata={messageType=SYSTEM}}, UserMessage{content='帮我解释一下:SpringAI有哪些优势', properties={messageType=USER}, messageType=USER}]

PromptTemplate

PromptTemplate族谱

  • PromptTemplate是提示词模版化技术,可以有效简化结构化提示词。

  • 下面IDEA生成是类图

    类图

  • 最顶端三个Actions的能力分别可以生成字符串、创建Prompt、创建Message

  • PromptTemplate具备构造者模式,这里如果创建Prompt或者Message角色都是user

  • PromptTemplate的子类

    • 子类其实有三个,分别就是另外三个角色类型的PromptTemplate

    • 子类创建出来的Message都是对应的角色的。类图中只体现了SystemPromptTemplate对应就是system角色的。

    • 这里其实有点问题,子类中都没有重写PromptTemplate的构造者模式,如果使用构造者则默认创建了PromptTemplate,然后创建的Message又成了user的。

      image

    • 所以子类不能使用构造者模式来创建。

使用PromptTemplate

  • 使用PromptTemplate来创建Message

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public void promptTemplateExample(String userMsg, UserInfo userInfo) {
    // 通过模版来创建 UserMessage
    Message user = PromptTemplate.builder()
    .template("亲爱的小助手,{msg}")
    .variables(Map.of("msg", userMsg))
    .build()
    .createMessage();

    log.info("\nPromptTemplate createMessage -> {}", user);

    String systemTemplate = "你是一位万能小助手。\n回答用户问题时必须要以“尊敬的用户:XXX,你好!”开头。\n用户信息:{info}";
    // 通过模版来创建 SystemMessage,子类只能通过new
    Message system = new SystemPromptTemplate(systemTemplate)
    .createMessage(Map.of("info", userInfo));

    log.info("\nSystemPromptTemplate createMessage -> {}", system);

    promptClient.prompt()
    .messages(system, user)
    .call()
    .content();
    }
  • 调用案例

    1
    2
    3
    4
    @Test
    public void promptTemplateExample() {
    promptExample.promptTemplateExample("你认识我吗?", new UserInfo("重案组之虎曹达华"));
    }
  • Message的输出。可以看到时按照模版来生成的Message

    1
    2
    3
    4
    5
    6
    2025-07-02T16:04:08.356+08:00  INFO 22082 --- [spring-ai-example] [           main] c.s.ai.example.prompt.two.PromptExample  : 
    PromptTemplate createMessage -> UserMessage{content='亲爱的小助手,你认识我吗?', properties={messageType=USER}, messageType=USER}
    2025-07-02T16:04:08.360+08:00 INFO 22082 --- [spring-ai-example] [ main] c.s.ai.example.prompt.two.PromptExample :
    SystemPromptTemplate createMessage -> SystemMessage{textContent='你是一位万能小助手。
    回答用户问题时必须要以“尊敬的用户:XXX,你好!”开头。
    用户信息:UserInfo[name=重案组之虎曹达华]', messageType=SYSTEM, metadata={messageType=SYSTEM}}
  • AI的输出

    1
    2
    3
    4
    5
    6
    7
    025-07-02T16:04:18.082+08:00  INFO 22082 --- [spring-ai-example] [           main] c.s.a.e.advisor.three.LogExampleAdvisor  : 
    Chat client response from AI
    output text -> 尊敬的用户:重案组之虎曹达华,你好!

    我当然认识您!您可是香港电影《逃学威龙》系列中赫赫有名的重案组之虎曹达华,周星驰电影里的经典角色。您以"软饭硬吃"的独特风格和"我系重案组总督察黄启发"的经典台词闻名,是无数影迷心中的喜剧警探形象代表。

    请问有什么可以为您效劳的吗?是要讨论案情还是需要生活上的帮助?

使用模版文件

  • 在项目的resources下创建./prompts/promptTemplateResourceExample.st文件

    image

  • promptTemplateResourceExample.st模版内容

    1
    你是一位万能小助手。回答用户问题时必须要以“尊敬的用户:XXX,你好!”开头。用户信息:{info}
  • Code。需要用到org.springframework.core.io.Resource

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Value("classpath:/prompts/promptTemplateResourceExample.st")
    private Resource promptTemplateResource;

    public void promptTemplateResourceExample(String userMsg, UserInfo userInfo) {
    Prompt prompt = new SystemPromptTemplate(promptTemplateResource)
    .create(Map.of("info", userInfo));
    log.info("\nSystemPromptTemplate resource createMessage -> {}", prompt.getSystemMessage());

    Message user = PromptTemplate.builder()
    .template("亲爱的小助手,{msg}")
    .variables(Map.of("msg", userMsg))
    .build()
    .createMessage();

    promptClient.prompt(prompt)
    .messages(user)
    .call()
    .content();
    }
  • 调用案例

    1
    2
    3
    4
    @Test
    public void promptTemplateResourceExample() {
    promptExample.promptTemplateResourceExample("你也听说过我的故事?", new UserInfo("重案组之虎曹达华"));
    }
  • 输出。完全按照模版来生成提示词的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    2025-07-02T16:13:45.836+08:00  INFO 22313 --- [spring-ai-example] [           main] c.s.ai.example.prompt.two.PromptExample  : 
    SystemPromptTemplate resource createMessage -> SystemMessage{textContent='你是一位万能小助手。回答用户问题时必须要以“尊敬的用户:XXX,你好!”开头。用户信息:UserInfo[name=重案组之虎曹达华]', messageType=SYSTEM, metadata={messageType=SYSTEM}}
    2025-07-02T16:13:45.872+08:00 INFO 22313 --- [spring-ai-example] [ main] c.s.a.e.advisor.three.LogExampleAdvisor :
    Chat client request to AI
    prompt text -> 你是一位万能小助手。回答用户问题时必须要以“尊敬的用户:XXX,你好!”开头。用户信息:UserInfo[name=重案组之虎曹达华]亲爱的小助手,你也听说过我的故事?
    context -> {
    "ClientName" : "promptClient"
    }
    2025-07-02T16:13:53.874+08:00 INFO 22313 --- [spring-ai-example] [ main] c.s.a.e.advisor.three.LogExampleAdvisor :
    Chat client response from AI
    output text -> 尊敬的用户:重案组之虎曹达华,你好!当然听说过您的传奇故事啦!您可是警界赫赫有名的"重案组之虎",破获过无数大案要案。不知道今天有什么可以为您效劳的?是遇到了棘手的案件需要分析,还是想聊聊您的光辉事迹?

修改模版的引用占位符

  • 从前面的案例可以看到模版默认是使用{}作为引用占位符的。比如{msg}{info}

  • 如果有特殊要求是可以通过TemplateRenderer修改成自定义的,比如修改成<>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public void promptTemplateRendererExample() {
    Message user = PromptTemplate.builder()
    // 自定义模版占位符规则
    .renderer(StTemplateRenderer.builder()
    .startDelimiterToken('<')
    .endDelimiterToken('>')
    .build())
    .template("亲爱的小助手,<msg>")
    .variables(Map.of("msg", "你在教我做事啊?"))
    .build()
    .createMessage();
    log.info("\nPromptTemplateRenderer createMessage -> {}", user);
    }
  • 输出结果。可以看到是一样的效果。

    1
    2
    2025-07-02T16:21:15.398+08:00  INFO 22467 --- [spring-ai-example] [           main] c.s.ai.example.prompt.two.PromptExample  : 
    PromptTemplateRenderer createMessage -> UserMessage{content='亲爱的小助手,你在教我做事啊?', properties={messageType=USER}, messageType=USER}
  • 这里好像也只能PromptTemplate可以通过TemplateRenderer修改,子类不行。

  • 也是因为子类没有重写构造者的原因,然后也没有提供set方法,导致无该能力。等后续SpringAI版本升级吧。

总结

  • 可以通过Prompt来操作Message。这个在Advisor的案例中就已经使用过的。
  • 可以通过PromptTemplate来创建Prompt或者Message,达到简化结构化提示词。
    • 可以通过模版文本。
    • 也可以通过模版文件。
    • PromptTemplate的子类没有重写构造者模式,等于是被阉割了。
  • 可以通过TemplateRenderer自定义模版的引用占位符。PromptTemplate的子类也无法使用。

最后

  • 提示词是使用AI非常重要的工程,提示词的质量和结构显著影响 AI 输出的有效性。

  • 如何创建高效的提示词SpringAI官方也有一些推荐

    • 指令:向 AI 提供清晰直接的指示,类似于与人沟通的方式。这种明确性对帮助AI “理解” 预期目标至关重要。
    • 外部上下文:必要时包含相关背景信息或对 AI 响应的具体指导。这种 “外部上下文” 构建了提示框架,帮助 AI 理解整体场景。
    • 用户输入:这是直接部分 — 用户构成提示核心的明确请求或问题。
    • 输出指示器:这部分可能很棘手。它需要指定 AI 响应的期望格式(如 JSON),但需注意 AI 可能不会严格遵循该格式。例如,它可能在实际 JSON 数据前添加 “这是您的JSON” 等短语,或有时生成不准确的类 JSON 结构。
  • 另外关于提示词工程指南可以点击进去学习。

  • 这两篇主要记录了如何使用SpringAI结构化提示词与AI交互的,后面将继续学习SpringAI是如何结构化输出的。

  • 所有案例的源码,都会提交在GitHub上。包:com.spring.ai.example.prompt.two